﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;

namespace NDK.Utils
{
    public class AsyncTcpIpUtils
    {
        //private static log4net.ILog m_Logger = log4net.LogManager.GetLogger("BZ.Utils.AsyncTcpIpUtils");

        public class ClientAsync
        {
            private TcpClient client;
            /// <summary>
            /// 客户端连接完成、发送完成、连接异常或者服务端关闭触发的事件
            /// </summary>
           public event Action<TcpClient, EnSocketAction> Completed;
            /// <summary>
            /// 客户端接收消息触发的事件
            /// </summary>
            public event Action<string, string> Received;
            /// <summary>
            /// 用于控制异步接收消息
            /// </summary>
            private ManualResetEvent doReceive = new ManualResetEvent(false);
            //标识客户端是否关闭
            private bool isClose = false;
            public ClientAsync()
            {
                client = new TcpClient();
                
            }

            /// <summary>
            /// 异步连接
            /// </summary>
            /// <param name="ip">要连接的服务器的ip地址</param>
            /// <param name="port">要连接的服务器的端口</param>
            public bool ConnectAsync(string ip, int port)
            {
                IPAddress ipAddress = null;
                try
                {
                    ipAddress = IPAddress.Parse(ip);
                }
                catch (Exception)
                {
                    //m_Logger.Error("ip地址格式不正确，请使用正确的ip地址！");
                    //Common.AppendUiLog.Log(ip+"ip地址格式不正确，请使用正确的ip地址！", Logger.LogType.Info);
                    return false;
                }

                doReceive.Reset();
                client.BeginConnect(ipAddress, port, ConnectCallBack, client);
                doReceive.WaitOne(5000 , false); //等待5s连接时间

                if (!client.Connected)
                {
                    //m_Logger.Error("连接服务器"+ ip + "连接超时");
                    //Common.AppendUiLog.Log("连接服务器" + ip + "连接超时...", Logger.LogType.Info);
                    return false;
                }
                else
                {
                    //Common.AppendUiLog.Log("TCP连接服务器"+ip + "成功...", Color.Black);
                    return true;
                }
            }

            /// <summary>
            /// 异步连接，连接ip地址为127.0.0.1
            /// </summary>
            /// <param name="port">要连接服务端的端口</param>
            public bool ConnectAsync(int port)
            {
                bool connectResult = false;
                connectResult = ConnectAsync("127.0.0.1", port);
                if (connectResult)
                {
                    return true;
                }
                else
                { return false; }
            }

            /// <summary>
            /// 异步断开连接
            /// </summary>
            /// <returns></returns>
            public void Disconnect()
            {
                // client.Client.BeginDisconnect();
                try
                {
                    client.Client.Shutdown(SocketShutdown.Both);
                    client.Client.Disconnect(true);
                    client.Client.BeginDisconnect(true, new AsyncCallback(DisconnectCallback), client.Client);
                    client.Dispose();
                }
                catch (Exception ex)
                {

                }
            }

            private void DisconnectCallback(IAsyncResult ar)
            {
                // Complete the disconnect request.
                try
                {
                    Socket client = (Socket)ar.AsyncState;
                    client.EndDisconnect(ar);
                }
                catch (Exception ex)
                {
                }

                // Signal that the disconnect is complete.
                // disconnectDone.Set();
            }


            /// <summary>
            /// 异步接收消息
            /// </summary>
            private void ReceiveAsync()
            {
                doReceive.Reset();
                StateObject obj = new StateObject();
                obj.Client = client;

                client.Client.BeginReceive(obj.ListData, 0, obj.ListData.Length, SocketFlags.None, ReceiveCallBack, obj);
                doReceive.WaitOne();
            }

            /// <summary>
            /// 异步发送消息
            /// </summary>
            /// <param name="msg"></param>
            public void SendAsync(string msg)
            {
                try
                {
                    byte[] listData = Encoding.UTF8.GetBytes(msg);
                    client.Client.BeginSend(listData, 0, listData.Length, SocketFlags.None, SendCallBack, client);
                }
                catch (Exception ex)
                {

                    //throw;
                }

            }

            /// <summary>
            /// 异步连接的回调函数
            /// </summary>
            /// <param name="ar"></param>
            private void ConnectCallBack(IAsyncResult ar)
            {
                TcpClient client = ar.AsyncState as TcpClient;
                try
                {
                    client.EndConnect(ar);

                    isClose = false;
                }
                catch (Exception EX)
                {
                    isClose = true;
                }

                OnComplete(client, EnSocketAction.Connect);
            }

            /// <summary>
            /// 异步接收消息的回调函数
            /// </summary>
            /// <param name="ar"></param>
            private void ReceiveCallBack(IAsyncResult ar)
            {
                StateObject obj = ar.AsyncState as StateObject;
                int count = -1;
                try
                {
                    count = obj.Client.Client.EndReceive(ar);
                    doReceive.Set();
                }
                catch (Exception ex)
                {
                    //如果发生异常，说明客户端失去连接，触发关闭事件
                    Close();
                    OnComplete(obj.Client, EnSocketAction.Close);
                }
                if (count > 0)
                {
                    string msg = Encoding.UTF8.GetString(obj.ListData, 0, count);
                    if (!string.IsNullOrEmpty(msg))
                    {
                        if (Received != null)
                        {
                            IPEndPoint iep = obj.Client.Client.RemoteEndPoint as IPEndPoint;
                            string key = string.Format("{0}:{1}", iep.Address, iep.Port);
                            Received(key, msg);
                        }
                    }
                }
                else if(count == 0)
                {
                    //如果发生异常，说明客户端失去连接，触发关闭事件
                    Close();
                    OnComplete(client, EnSocketAction.Close);
                }
            }

            /// <summary>
            /// 异步发送消息的回调函数
            /// </summary>
            /// <param name="ar"></param>
            private void SendCallBack(IAsyncResult ar)
            {
                TcpClient client = ar.AsyncState as TcpClient;
                try
                {
                    client.Client.EndSend(ar);
                    OnComplete(client, EnSocketAction.SendMsg);
                }
                catch (Exception ex)
                {
                    //如果发生异常，说明客户端失去连接，触发关闭事件
                    Close();
                    OnComplete(client, EnSocketAction.Close);
                }
            }

            public virtual void OnComplete(TcpClient client, EnSocketAction enAction)
            {
                if (Completed != null)
                    Completed(client, enAction);
                if (enAction == EnSocketAction.Connect)//建立连接后，开始接收数据
                {
                    ThreadPool.QueueUserWorkItem(x =>
                    {
                        while (!isClose)
                        {
                            try
                            {
                                Thread.Sleep(20);
                                ReceiveAsync();
                                Thread.Sleep(20);
                            }
                            catch (Exception ex)
                            {
                                Close();
                                OnComplete(client, EnSocketAction.Close);
                            }
                        }
                    });
                }
            }

            public void Close()
            {
                isClose = true;
            }
        }


        /// <summary>
        /// 服务器端异步通信类
        /// </summary>
        public class ServerAsync
        {
            private TcpListener listener = null;

            //用于控制异步接受连接
            private ManualResetEvent doConnect = new ManualResetEvent(false);
            //用于控制异步接收数据
            private ManualResetEvent doReceive = new ManualResetEvent(false);
            //标识服务端连接是否关闭
            private bool isClose = false;
            private Dictionary<string, TcpClient> listClient = new Dictionary<string, TcpClient>();
            /// <summary>
            /// 已建立连接的集合
            /// key:ip:port
            /// value:TcpClient
            /// </summary>
            public Dictionary<string, TcpClient> ListClient
            {
                get { return listClient; }
                private set { listClient = value; }
            }
            /// <summary>
            /// 连接、发送、关闭事件
            /// </summary>
            public event Action<string, EnSocketAction> Completed;
            /// <summary>
            /// 接收到数据事件
            /// </summary>
            public event Action<string, string> Received;
            public ServerAsync()
            {

            }
            /// <summary>
            /// 开始异步监听ip地址的端口
            /// </summary>
            /// <param name="ip"></param>
            /// <param name="port"></param>
            public void StartAsync(string ip, int port)
            {
                isClose = false;
                IPAddress ipAddress = null;
                try
                {
                    ipAddress = IPAddress.Parse(ip);
                }
                catch (Exception e)
                {
                    throw e;
                }
                listener = new TcpListener(new IPEndPoint(ipAddress, port));
                listener.Start();
                ThreadPool.QueueUserWorkItem(x =>
                {
                    while (!isClose)
                    {
                        if (!listener.Pending())
                        {
                            //为了避免每次都被tcpListener.AcceptTcpClient()阻塞线程，添加了此判断，
                            //no connection requests have arrived。
                            //当没有连接请求时，什么也不做，有了请求再执行到tcpListener.AcceptTcpClient()
                        }
                        else
                        {
                            doConnect.Reset();
                            listener.BeginAcceptTcpClient(AcceptCallBack, listener);
                            doConnect.WaitOne();
                        }
                    }
                });
            }
            /// <summary>
            /// 开始异步监听本机127.0.0.1的端口号
            /// </summary>
            /// <param name="port"></param>
            public void StartAsync(int port)
            {
                StartAsync("127.0.0.1", port);
            }
            /// <summary>
            /// 开始异步发送数据
            /// </summary>
            /// <param name="key">客户端的ip地址和端口号</param>
            /// <param name="msg">要发送的内容</param>
            public void SendAsync(string key, string msg)
            {
                if (!ListClient.ContainsKey(key))
                {
                    //throw new Exception("所用的socket不在字典中,请先连接！");
                    //m_Logger.Error("所用的socket不在字典中,请先连接！");
                    //Common.AppendUiLog.Log("所用的socket不在字典中,请先连接！", Logger.LogType.Alert);
                    return;
                }
                TcpClient client = ListClient[key];
                byte[] listData = Encoding.UTF8.GetBytes(msg);
                client.Client.BeginSend(listData, 0, listData.Length, SocketFlags.None, SendCallBack, client);
            }
            /// <summary>
            /// 开始异步接收数据
            /// </summary>
            /// <param name="key">要接收的客户端的ip地址和端口号</param>
            private void ReceiveAsync(string key)
            {
                doReceive.Reset();
                if (ListClient.ContainsKey(key))
                {
                    TcpClient client = ListClient[key];
                    //if (!client.Connected)
                    //{
                    //    ListClient.Remove(key);
                    //    OnComplete(key, EnSocketAction.Close);
                    //    return;
                    //}
                    StateObject obj = new StateObject();
                    obj.Client = client;
                    try
                    {
                        client.Client.BeginReceive(obj.ListData, 0, obj.ListData.Length, SocketFlags.None, ReceiveCallBack, obj);
                    }
                    catch (Exception)
                    {

                    }
                    doReceive.WaitOne();
                }
            }
            /// <summary>
            /// 异步接收连接的回调函数
            /// </summary>
            /// <param name="ar"></param>
            private void AcceptCallBack(IAsyncResult ar)
            {

                TcpListener l = ar.AsyncState as TcpListener;
                TcpClient client = l.EndAcceptTcpClient(ar);
                doConnect.Set();

                IPEndPoint iep = client.Client.RemoteEndPoint as IPEndPoint;
                string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);
                if (!ListClient.ContainsKey(key))
                {
                    ListClient.Add(key, client);
                    OnComplete(key, EnSocketAction.Connect);
                }

            }
            /// <summary>
            /// 异步发送数据的回调函数
            /// </summary>
            /// <param name="ar"></param>
            private void SendCallBack(IAsyncResult ar)
            {
                TcpClient client = ar.AsyncState as TcpClient;
                IPEndPoint iep = client.Client.RemoteEndPoint as IPEndPoint;
                string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);
                if (Completed != null)
                {
                    Completed(key, EnSocketAction.SendMsg);
                }

            }
            /// <summary>
            /// 异步接收数据的回调函数
            /// </summary>
            /// <param name="ar"></param>
            private void ReceiveCallBack(IAsyncResult ar)
            {
                StateObject obj = ar.AsyncState as StateObject;

                int count = -1;
                try
                {
                    count = obj.Client.Client.EndReceive(ar);
                }
                catch (Exception e)
                {
                    if (!obj.Client.Client.Connected)
                    {
                        IPEndPoint iep = obj.Client.Client.RemoteEndPoint as IPEndPoint;
                        string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);

                        ListClient.Remove(key);
                        OnComplete(key, EnSocketAction.Close);
                        doReceive.Set();
                        return;
                    }
                }
                doReceive.Set();
                if (count > 0)
                {
                    string msg = Encoding.UTF8.GetString(obj.ListData, 0, count);
                    if (!string.IsNullOrEmpty(msg))
                    {
                        if (Received != null)
                        {
                            IPEndPoint iep = obj.Client.Client.RemoteEndPoint as IPEndPoint;
                            string key = string.Format("{0}:{1}", iep.Address.ToString(), iep.Port);
                            Received(key, msg);//触发接收事件
                        }
                    }
                }
            }
            public virtual void OnComplete(string key, EnSocketAction enAction)
            {
                if (Completed != null)
                    Completed(key, enAction);
                if (enAction == EnSocketAction.Connect)//当连接建立时，则要一直接收
                {
                    ThreadPool.QueueUserWorkItem(x =>
                    {
                        while (ListClient.ContainsKey(key) && !isClose)
                        {
                            Thread.Sleep(20);
                            ReceiveAsync(key);
                            Thread.Sleep(20);
                        }
                    });

                }
            }
            public void Close()
            {
                isClose = true;
                listener.Stop();
            }
        }

        /// <summary>
        /// 接收socket的行为
        /// </summary>
        public enum EnSocketAction
        {
            /// <summary>
            /// socket发生连接
            /// </summary>
            Connect = 1,
            /// <summary>
            /// socket发送数据
            /// </summary>
            SendMsg = 2,
            /// <summary>
            /// socket关闭
            /// </summary>
            Close = 4,

            /// <summary>
            /// socket关闭
            /// </summary>
            DisConnect = 3
        }
        /// <summary>
        /// 对异步接收时的对象状态的封装，将socket与接收到的数据封装在一起
        /// </summary>
        public class StateObject
        {
            public TcpClient Client { get; set; }
            private byte[] listData = new byte[2048];
            /// <summary>
            /// 接收的数据
            /// </summary>
            public byte[] ListData
            {
                get
                {
                    return listData;
                }
                set
                {
                    listData = value;
                }
            }
        }

    }
}
